package com.qfmy.webapp.lucene;

import com.qfmy.model.entity.novel.Novel;
import com.qfmy.model.enums.NovelStatus;
import com.qfmy.model.vo.novel.NovelVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.jetbrains.annotations.NotNull;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * Lucene工具类：仅支持书籍索引创建、搜索及搜索建议
 */
@Slf4j
@SuppressWarnings("all")
public class LuceneUtils {
    // 内存索引存储（仅保留书籍索引）
    private static final Directory novelIndex = new RAMDirectory();
    // 共享分词器（标准分词，支持英文/数字分词，中文需额外配置IKAnalyzer）
    private static final StandardAnalyzer analyzer = new StandardAnalyzer();


    // ============================ 索引创建方法 ============================

    /**
     * 创建书籍索引（支持书名、作者、类型等字段分词搜索）
     * @param novels 书籍实体列表
     * @throws Exception 索引创建异常
     */
    public static void createNovelIndex(List<Novel> novels) throws Exception {
        if (novels == null || novels.isEmpty()) {
            log.warn("书籍索引创建：传入的书籍列表为空");
            return;
        }

        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        IndexWriter writer = new IndexWriter(novelIndex, config);

        for (Novel novel : novels) {
            Document doc = new Document();
            // 核心字段：ID（精确匹配）
            doc.add(new StringField("book_id", String.valueOf(novel.getNovelId()), Field.Store.YES));
            // 核心搜索字段：书名（分词）
            doc.add(new TextField("book_name", novel.getNovelName(), Field.Store.YES));
            // 核心搜索字段：作者名（分词）
            doc.add(new TextField("authorName", novel.getAuthorName(), Field.Store.YES));
            // 核心搜索字段：书籍类型（分词，支持类型搜索）
            doc.add(new TextField("type", novel.getCategoryName(), Field.Store.YES));
            // 核心搜索字段：书籍描述（分词）
            doc.add(new TextField("des", novel.getDes(), Field.Store.YES));
            // 辅助字段：封面URL
            doc.add(new StringField("img_url", novel.getImage(), Field.Store.YES));
            // 辅助字段：状态（枚举转名称）
            doc.add(new StringField("status", NovelStatus.getNameByCode(novel.getStatus()), Field.Store.YES));
            // 数值字段：评分（支持排序+存储展示）
            doc.add(new DoublePoint("total_score", novel.getScore().doubleValue()));
            doc.add(new StringField("total_score_str", novel.getScore().toString(), Field.Store.YES));
            // 数值字段：阅读量
            doc.add(new IntPoint("read_book", novel.getViewCount()));
            doc.add(new StringField("read_book_str", String.valueOf(novel.getViewCount()), Field.Store.YES));
            // 数值字段：评分人数
            doc.add(new IntPoint("rating_count", Math.toIntExact(novel.getScoreCount())));
            doc.add(new StringField("rating_count_str", String.valueOf(novel.getScoreCount()), Field.Store.YES));

            writer.addDocument(doc);
        }

        writer.close();
        log.info("书籍索引创建完成，共索引 {} 条数据", novels.size());
    }


    // ============================ 搜索方法 ============================

    /**
     * 搜索书籍（多字段匹配：书名、作者、类型、描述）
     * @param keyword 搜索关键词
     * @return 书籍VO列表（按匹配度排序，取前20条）
     * @throws Exception 搜索异常
     */
    @NotNull
    public static List<NovelVo> searchNovels(String keyword) throws Exception {
        List<NovelVo> results = new ArrayList<>();
        // 前置检查：索引不存在直接返回空
        if (!isIndexExists(novelIndex)) {
            log.debug("小说索引为空，搜索无结果");
            return results;
        }

        IndexReader reader = null;
        try {
            reader = DirectoryReader.open(novelIndex);
            IndexSearcher searcher = new IndexSearcher(reader);

            // 多字段搜索配置（权重一致，如需权重可使用Map配置boost）
            String[] searchFields = {"book_name", "authorName", "type", "des"};
            MultiFieldQueryParser parser = new MultiFieldQueryParser(searchFields, analyzer);
            Query query = parser.parse(keyword);

            // 执行搜索（取前20条）
            TopDocs topDocs = searcher.search(query, 20);

            // 转换为VO
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                NovelVo vo = new NovelVo();
                vo.setNovelId(Integer.parseInt(doc.get("book_id")));
                vo.setNovelName(doc.get("book_name"));
                vo.setAuthorName(doc.get("authorName"));
                vo.setCategoryName(doc.get("type"));
                vo.setDes(doc.get("des"));
                vo.setImage(doc.get("img_url"));
                vo.setStatus(doc.get("status"));
                vo.setScore(new BigDecimal(doc.get("total_score_str")));
                vo.setViewCount(Integer.parseInt(doc.get("read_book_str")));
                vo.setScoreCount(Long.valueOf(doc.get("rating_count_str")));
                results.add(vo);
            }
        } finally {
            closeReader(reader); // 确保资源关闭
        }

        return results;
    }


    // ============================ 搜索建议方法 ============================

    /**
     * 获取书籍搜索建议（前缀匹配书名、作者、类型）
     * @param keyword 输入的关键词前缀（如"少年"）
     * @return 去重后的建议列表（按匹配度排序）
     * @throws Exception 搜索异常
     */
    public static List<String> getBookSearchSuggestions(String keyword) throws Exception {
        if (keyword == null || keyword.trim().isEmpty()) {
            return new ArrayList<>(); // 空关键词返回空建议
        }
        keyword = keyword.trim();

        // 仅保留书籍建议（已移除视频相关）
        List<String> suggestions = getNovelSuggestions(keyword);

        // 去重（保留首次出现的顺序）
        return new ArrayList<>(new LinkedHashSet<>(suggestions));
    }

    /**
     * 书籍搜索建议（前缀匹配书名、作者、类型）
     */
    private static List<String> getNovelSuggestions(String keyword) throws Exception {
        List<String> suggestions = new ArrayList<>();
        // 前置检查：索引不存在直接返回空（修复原报错核心）
        if (!isIndexExists(novelIndex)) {
            log.debug("小说索引为空，跳过搜索建议");
            return suggestions;
        }

        IndexReader reader = null;
        try {
            reader = DirectoryReader.open(novelIndex);
            IndexSearcher searcher = new IndexSearcher(reader);

            // 前缀匹配的字段（优先级：书名 > 作者 > 类型）
            String[] fields = {"book_name", "authorName", "type"};
            for (String field : fields) {
                // 前缀查询（匹配以keyword开头的内容）
                PrefixQuery query = new PrefixQuery(new Term(field, keyword));
                TopDocs topDocs = searcher.search(query, 5); // 每个字段取前5条

                for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                    Document doc = searcher.doc(scoreDoc.doc);
                    suggestions.add(doc.get(field)); // 直接添加字段值作为建议
                }
            }
        } finally {
            closeReader(reader);
        }
        return suggestions;
    }


    // ============================ 工具方法 ============================

    /**
     * 检查索引目录是否包含有效索引
     * @param directory 索引目录
     * @return true=存在有效索引，false=无索引/空目录
     */
    private static boolean isIndexExists(Directory directory) {
        try {
            // 检查目录是否有segments文件（Lucene索引的核心文件）
            return DirectoryReader.indexExists(directory);
        } catch (Exception e) {
            log.warn("检查索引存在性失败", e);
            return false;
        }
    }

    /**
     * 安全关闭IndexReader（避免资源泄漏）
     */
    private static void closeReader(IndexReader reader) {
        if (reader != null) {
            try {
                reader.close();
            } catch (Exception e) {
                log.error("关闭索引读取器失败", e);
            }
        }
    }
}